package logic

import (
	"core/card"
	"core/command"
	"core/common"
	"core/component/idgenerator"
	"core/component/logger"
	"core/helper/util"
	"math/rand"
	"sync"
	"time"
)

const (
	EveryRoundMaxWaitTime = 20
	PlayerHandCardNum     = 17
)

type Room struct {
	rmSrv                    *RoomSrv
	roomId                   uint64
	players                  *sync.Map     //map[playerId]*RoomPlayer
	landowner                uint64        //地主id
	lastOutOfCardPlayerId    uint64        //上一位出牌玩家id
	lastOutOfCards           []string      //上一位所出牌
	underCards               []string      //
	rPlayerIds               []uint64      //玩家顺序
	playerOutOfCardStartTime int64         //玩家出牌开始时间
	alreadyOutOfPlayerId     uint64        //已出牌玩家
	cardGroupManager         *CardsManager //牌组管理
}

func newRoom(roomId uint64, players []*Player, rmSrv *RoomSrv) *Room {
	rm := &Room{
		rmSrv:            rmSrv,
		roomId:           roomId,
		cardGroupManager: NewCardManager(),
		players:          &sync.Map{},
	}
	rm.addRoomPlayer(players)
	return rm
}

func (rm *Room) playerLeave(playerId uint64) {
	p, ok := rm.players.Load(playerId)
	if !ok {
		return
	}
	p.(*RoomPlayer).setRobot()
}

func (rm *Room) checkOutOfCardTimeOut() {
	now := time.Now().Unix()
	if rm.playerOutOfCardStartTime != 0 && now-rm.playerOutOfCardStartTime >= EveryRoundMaxWaitTime {
		rm.timeOutPlayerOutOfCards()
		rm.playerOutOfCardStartTime = now
	}
}

func (rm *Room) sendRoomMsg(cmd command.Command, msg interface{}) {
	rm.players.Range(func(key, value any) bool {
		rp, ok := value.(*RoomPlayer)
		if ok && !rp.robot() {
			rp.owner.SendMsgToClient(cmd, msg)
		}
		return true
	})
}

func (rm *Room) sendRoomOnePlayerMsg(cmd command.Command, msg interface{}, playerId uint64) {
	rm.players.Range(func(key, value any) bool {
		rp, ok := value.(*RoomPlayer)
		if ok && rp.owner.playerId == playerId {
			rp.owner.SendMsgToClient(cmd, msg)
			return true
		}
		return true
	})
}

func (rm *Room) getRoomPlayer(playerId uint64) *RoomPlayer {
	p, ok := rm.players.Load(playerId)
	if !ok {
		return nil
	}
	return p.(*RoomPlayer)
}

// 设置地主id
func (rm *Room) setLandowner(id uint64) {
	rm.landowner = id
}

func (rm *Room) addRoomPlayer(players []*Player) {
	temp := make([]*TempPlayer, 0)
	for _, v := range players {
		temp = append(temp, &TempPlayer{p: v})
	}
	for i := 0; i < 3-len(players); i++ {
		temp = append(temp, &TempPlayer{p: &Player{playerId: idgenerator.FetchID(),
			nickName: util.NameMgrGetMe().RandName()}, isRobot: true})
	}

	msgSend := &command.SCStartPvp{
		RoomId: rm.roomId,
	}
	for _, p := range temp {
		rp := newRoomPlayer(p, rm.cardGroupManager.cards[:PlayerHandCardNum])
		rp.owner.roomId = rm.roomId
		rm.players.Store(p.p.playerId, rp)
		rm.cardGroupManager.cards = rm.cardGroupManager.cards[PlayerHandCardNum:]
		rm.rPlayerIds = append(rm.rPlayerIds, p.p.playerId)
	}
	rm.underCards = rm.cardGroupManager.cards
	rm.landowner = rm.rPlayerIds[rand.Intn(len(rm.rPlayerIds))]
	rm.playerOutOfCardStartTime = time.Now().Unix()
	rp, ok := rm.players.Load(rm.landowner)
	if ok {
		rp.(*RoomPlayer).addCards(rm.underCards)
	}
	rm.players.Range(func(key, value interface{}) bool {
		p, ok1 := value.(*RoomPlayer)
		if !ok1 {
			return true
		}
		pla := &command.Players{
			PlayerId: p.owner.playerId,
			Cards:    util.Map2Array(p.cards),
			NickName: p.owner.nickName,
		}
		msgSend.Players = append(msgSend.Players, pla)
		return true
	})
	msgSend.UnderCards = rm.underCards
	msgSend.Landowner = rm.landowner
	rm.sendRoomMsg(command.Command_SCStartPvp_Pvp, msgSend)
	if ok && rp.(*RoomPlayer).robot() {
		rm.addRobotChannel(rp.(*RoomPlayer))
	}
}

// 出牌
func (rm *Room) outOfCards(cards []string, rp *RoomPlayer, cardType command.CardType) {
	msgSend := &command.SCOutOfCardsPvp{
		PlayerId: rp.owner.playerId,
		Cards:    cards,
		Ret:      true,
	}
	rm.alreadyOutOfPlayerId = rp.owner.playerId
	rm.playerOutOfCardStartTime = time.Now().Unix()
	if len(cards) != 0 {
		rm.lastOutOfCardPlayerId = rp.owner.playerId
		rm.lastOutOfCards = cards
		rp.delCards(cards)
	}
	//logger.Infof("playerId %v cardsLen %v", rp.owner.playerId, len(rp.cards))
	msgSend.CardType = cardType
	if len(rp.cards) == 0 {
		msgSend.IsGameOver = true
		otherPlayerIds := rm.getOtherPlayers(rp.owner.playerId)
		msgSend.GEndInfo = append(msgSend.GEndInfo, &command.GameEndInfo{PlayerId: rp.owner.playerId, WinOrLose: true})
		if rp.owner.playerId == rm.landowner {
			for _, playerId := range otherPlayerIds {
				msgSend.GEndInfo = append(msgSend.GEndInfo, &command.GameEndInfo{PlayerId: playerId, WinOrLose: false})
			}
		} else {
			for _, playerId := range otherPlayerIds {
				if playerId == rm.landowner {
					msgSend.GEndInfo = append(msgSend.GEndInfo, &command.GameEndInfo{PlayerId: playerId, WinOrLose: false})
				} else {
					msgSend.GEndInfo = append(msgSend.GEndInfo, &command.GameEndInfo{PlayerId: playerId, WinOrLose: true})
				}
			}
		}
		logger.Infof("[游戏结束] 胜者玩家playerID:%v 名字:%v roomID:%v", rp.owner.playerId, rp.owner.nickName, rm.roomId)
	}
	rm.sendRoomMsg(command.Command_SCOutOfCardsInfo_Pvp, msgSend)
	if !msgSend.IsGameOver {
		nextP := rm.getNextPlayer(rp.owner.playerId)
		if nextP == nil || !nextP.robot() {
			return
		}
		rm.addRobotChannel(nextP)
	} else {
		rm.rmSrv.sendRemoveRoom(rm.roomId)
		logger.Infof("游戏结束 移除房间 roomID:%v", rm.roomId)
	}
}

func (rm *Room) judgeOutCardSuccess(playerId uint64, cards []string) bool {
	if rm.lastOutOfCardPlayerId == 0 {
		return true
	}
	if rm.lastOutOfCardPlayerId == playerId {
		return true
	} else if len(cards) == 0 {
		return true
	}
	cr := card.CardsMgrGetMe().CardTurnOff(cards)
	ret, _ := card.CardsMgrGetMe().ComparePoker(card.CardsMgrGetMe().CardTurnOff(rm.lastOutOfCards), cr)
	return ret > 0
}

func (rm *Room) getOtherPlayers(playerId uint64) []uint64 {
	otherPlayerId := make([]uint64, 0)
	for _, id := range rm.rPlayerIds {
		if id != playerId {
			otherPlayerId = append(otherPlayerId, id)
		}
	}
	return otherPlayerId
}

func (rm *Room) getNextPlayer(id uint64) *RoomPlayer {
	index := 0
	for k, pid := range rm.rPlayerIds {
		if pid == id {
			if k == len(rm.rPlayerIds)-1 {
				index = 0
				break
			} else {
				index = k + 1
				break
			}
		}
	}
	val, ok := rm.players.Load(rm.rPlayerIds[index])
	if !ok {
		return nil
	}
	return val.(*RoomPlayer)
}

func (rm *Room) addRobotChannel(msg *RoomPlayer) {
	rm.rmSrv.sendRobotChanMsg(&common.RobotChanMsg{RoomId: rm.roomId, Info: msg})
}

// 机器人出牌
func (rm *Room) robotOutOfCards(rp *RoomPlayer) {
	//var isRand bool
	waitTime := time.Second * time.Duration(util.RandBetweenInt32(2, 7))
	time.Sleep(waitTime)
	cards := make([]string, 0)
	handCards := rp.cardsToArray()
	if rm.lastOutOfCardPlayerId == rp.owner.playerId || rm.lastOutOfCardPlayerId == 0 {
		above := card.CardsMgrGetMe().RobotRandCards(card.CardsMgrGetMe().CardTurnOff(handCards))
		cards = rp.intArrayTurnOffStrArray(above)
		//isRand = true
	} else {
		above := card.CardsMgrGetMe().CardsAbove(card.CardsMgrGetMe().CardTurnOff(handCards),
			card.CardsMgrGetMe().CardTurnOff(rm.lastOutOfCards))
		cards = rp.intArrayTurnOffStrArray(above)
	}
	var cardType command.CardType
	if len(cards) != 0 {
		cardTypeStr, _ := card.CardsMgrGetMe().PokersValue(card.CardsMgrGetMe().ToPokers(card.CardsMgrGetMe().CardTurnOff(cards)))
		cardType = card.CardsMgrGetMe().GetCardTypeByC(cardTypeStr)
	}

	//logger.Infof("[机器人出牌]isRand=%v waitTime=%v roomId=%v playerId=%v outCards=%v cardType=%v,lastOutOfCards=%v",
	//	isRand, waitTime, rm.roomId, rp.owner.playerId, cards, cardType, rm.lastOutOfCards)
	rm.outOfCards(cards, rp, cardType)
}

// 超时玩家出牌
func (rm *Room) timeOutPlayerOutOfCards() {
	var rp *RoomPlayer
	if rm.alreadyOutOfPlayerId == 0 {
		v, ok := rm.players.Load(rm.landowner)
		if !ok {
			return
		}
		rp = v.(*RoomPlayer)
	} else {
		rp = rm.getNextPlayer(rm.alreadyOutOfPlayerId)
	}
	if rp == nil {
		return
	}
	cards := make([]string, 0)
	handCards := rp.cardsToArray()
	if rm.lastOutOfCardPlayerId == rp.owner.playerId || rm.lastOutOfCardPlayerId == 0 {
		above := card.CardsMgrGetMe().RobotRandCards(card.CardsMgrGetMe().CardTurnOff(handCards))
		cards = rp.intArrayTurnOffStrArray(above)
	}
	var cardType command.CardType
	if len(cards) != 0 {
		cardTypeStr, _ := card.CardsMgrGetMe().PokersValue(card.CardsMgrGetMe().ToPokers(card.CardsMgrGetMe().CardTurnOff(cards)))
		cardType = card.CardsMgrGetMe().GetCardTypeByC(cardTypeStr)
	}
	//logger.Infof("[超时玩家出牌]roomId=%v playerId=%v outCards=%v cardType=%v,lastOutOfCards=%v",
	//	rm.roomId, rp.owner.playerId, cards, cardType, rm.lastOutOfCards)
	rm.outOfCards(cards, rp, cardType)
}

func (rm *Room) clearRoomPlayer() {
	rm.players.Range(func(key, value any) bool {
		p, ok := value.(*RoomPlayer)
		if !ok {
			return true
		}
		if p.owner != nil {
			p.owner.exitRoom()
		}
		p = nil
		return true
	})
}
